// Attribution:  Some of the content view logic was copied or inspired from the custom window article on Matt Gallagher's excellent Cocoa wih Love blog (http://cocoawithlove.com/2008/12/drawing-custom-window-on-mac-os-x.html)

#import "SMRPopoverFrameView.h"
#import "SMRPopoverWindow.h"


#pragma mark Constants

static const CGFloat SMRPopoverFrameViewArrowHeight = 16.0f;
static const CGFloat SMRPopoverFrameViewArrowWidth = 24.0f;
static const CGFloat SMRPopoverFrameViewBorderWidth = 0.0f;
static const CGFloat SMRPopoverFrameViewOuterCornerRadius = 12.0f;


#pragma mark -
@interface SMRPopoverFrameView ()
@property (nonatomic, assign) BOOL needsTile_;
@end


#pragma mark -
@interface SMRPopoverFrameView (SMRPopoverFrameViewPrivateMethods)
-(void)commonInit_;
-(void)tileContent_;
-(NSBezierPath *)framePath_;
-(NSBezierPath *)outerFramePath_;
-(void)appendArrowToOuterFramePath_:(NSBezierPath *)path peakAt:(NSPoint)peakPoint endAtPoint:(NSPoint)endPoint;
-(NSRect)frameRectWithoutArrow_;
@end


#pragma mark -
@implementation SMRPopoverFrameView

@synthesize arrowPosition;
@synthesize arrowDirection;
@synthesize outerCornerRadius;
@synthesize arrowSize;
@synthesize borderWidth;
@synthesize backgroundGradient;
@synthesize needsTile_;


#pragma mark -
#pragma mark Initialization

-(id)initWithFrame:(NSRect)frameRect {
	if (!(self = [super initWithFrame:frameRect]))
		return nil;
	
	[self setOuterCornerRadius:SMRPopoverFrameViewOuterCornerRadius];
	[self setArrowSize:NSMakeSize(SMRPopoverFrameViewArrowWidth, SMRPopoverFrameViewArrowHeight)];
	[self setBorderWidth:SMRPopoverFrameViewBorderWidth];
	NSColor *topColor = [NSColor colorWithCalibratedWhite:1.0f alpha:1.000]; 
	NSColor *bottomColor = [NSColor colorWithCalibratedWhite:0.92 alpha:1.000];
	NSGradient *backGradient = [[[NSGradient alloc] initWithStartingColor:topColor endingColor:bottomColor] autorelease];
	[self setBackgroundGradient:backGradient];
	[self setArrowPosition:frameRect.size.width/2];
	[self setArrowDirection:SMRPopoverArrowDirectionUp];
	
	return self;
}


#pragma mark -
#pragma mark Accessors

-(CGFloat)minimumArrowPositionFromEdge {
	return [self outerCornerRadius] + ([self arrowSize].width / 2);
}

-(void)setArrowDirection:(SMRPopoverArrowDirection)aDirection {
	if (arrowDirection != aDirection)
		[self setNeedsTile_:YES];
	arrowDirection = aDirection;
}

-(void)setArrowSize:(NSSize)aSize {
	if (!NSEqualSizes(aSize, arrowSize))
		[self setNeedsTile_:YES];
	arrowSize = aSize;
}

-(void)setBorderWidth:(CGFloat)width {
	if (borderWidth != width)
		[self setNeedsTile_:YES];
	borderWidth = width;
}


#pragma mark -
#pragma mark Drawing 

-(void)viewWillDraw {
	if ([self needsTile_])
		[self tileContent_];
	[super viewWillDraw];
}

-(void)drawRect:(NSRect)dirtyRect {
	NSBezierPath *framePath = [self framePath_];
	[[self backgroundGradient] drawInBezierPath:framePath angle:270.0f];
}


#pragma mark -
#pragma mark Layout

-(void)tileContent_ {
	NSAssert([[self subviews] count] == 1, @"SMRPopoverFrameView contains more than 1 subview.  Only designed for a single subview that is the window's true content view.");
	NSRect insetFrame = NSInsetRect([self frameRectWithoutArrow_], [self borderWidth], [self borderWidth]);
	[[[self subviews] objectAtIndex:0] setFrame:insetFrame];
}

-(NSRect)frameRectWithoutArrow_ {
	NSRect frameRect = [self bounds];
	CGFloat arrowHeight = [self arrowSize].height;
	switch ([self arrowDirection]) {
		case SMRPopoverArrowDirectionDown:
			frameRect.origin.y += arrowHeight;
			frameRect.size.height -= arrowHeight;
			break;
		case SMRPopoverArrowDirectionUp:
			frameRect.size.height -= arrowHeight;
			break;
		case SMRPopoverArrowDirectionRight:
			frameRect.size.width -= arrowHeight;
			break;
		case SMRPopoverArrowDirectionLeft:
			frameRect.origin.x += arrowHeight;
			frameRect.size.width -= arrowHeight;
		default:
			break;
	}
	return frameRect;
}

-(NSBezierPath *)framePath_ {
	NSBezierPath *outerFramePath = [self outerFramePath_];
	return outerFramePath;
}

-(NSBezierPath *)outerFramePath_ {
	NSRect frameRect = [self frameRectWithoutArrow_];
	NSBezierPath *outerFramePath = [NSBezierPath bezierPath];
	CGFloat minX = NSMinX(frameRect);
	CGFloat maxX = NSMaxX(frameRect);
	CGFloat minY = NSMinY(frameRect);
	CGFloat maxY = NSMaxY(frameRect);
	
	// Outer path - Start at top left, going clockwise
	NSPoint endPoint;
	CGFloat outerRadius = [self outerCornerRadius];
	CGFloat arrowHeight = [self arrowSize].height;
	[outerFramePath moveToPoint:NSMakePoint(minX + outerRadius, maxY)];
	// Top
	endPoint = NSMakePoint(maxX - outerRadius, maxY);
	if ([self arrowDirection] & SMRPopoverArrowDirectionUp) 
		[self appendArrowToOuterFramePath_:outerFramePath peakAt:NSMakePoint(arrowPosition, maxY + arrowHeight) endAtPoint:endPoint];
	else 
		[outerFramePath lineToPoint:endPoint];
	[outerFramePath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, maxY) toPoint:NSMakePoint(maxX, maxY - outerRadius) radius:outerRadius];
	
	// Right
	endPoint = NSMakePoint(maxX, minY + outerRadius);
	if ([self arrowDirection] & SMRPopoverArrowDirectionRight)
		[self appendArrowToOuterFramePath_:outerFramePath peakAt:NSMakePoint(maxX + arrowHeight, arrowPosition) endAtPoint:endPoint];
	else 
		[outerFramePath lineToPoint:endPoint];
	[outerFramePath appendBezierPathWithArcFromPoint:NSMakePoint(maxX, minY) toPoint:NSMakePoint(maxX - outerRadius, minY) radius:outerRadius];
	
	// Bottom
	endPoint = NSMakePoint(minX + outerRadius, minY);
	if ([self arrowDirection] & SMRPopoverArrowDirectionDown)
		[self appendArrowToOuterFramePath_:outerFramePath peakAt:NSMakePoint(arrowPosition, minY - arrowHeight) endAtPoint:endPoint];
	else
		[outerFramePath lineToPoint:endPoint];
	[outerFramePath appendBezierPathWithArcFromPoint:NSMakePoint(minX, minY) toPoint:NSMakePoint(minX, minY + outerRadius) radius:outerRadius];
	
	// Left
	endPoint = NSMakePoint(minX, maxY - outerRadius);
	if ([self arrowDirection] & SMRPopoverArrowDirectionLeft)
		[self appendArrowToOuterFramePath_:outerFramePath peakAt:NSMakePoint(minX - arrowHeight, arrowPosition) endAtPoint:endPoint];
	else 
		[outerFramePath lineToPoint:endPoint];
	[outerFramePath appendBezierPathWithArcFromPoint:NSMakePoint(minX, maxY) toPoint:NSMakePoint(minX + outerRadius, maxY) radius:outerRadius];
	[outerFramePath closePath];
	
	return outerFramePath;
}

-(void)appendArrowToOuterFramePath_:(NSBezierPath *)path peakAt:(NSPoint)peakPoint endAtPoint:(NSPoint)endPoint {
	NSPoint currentPoint = [path currentPoint];
	CGFloat halfArrowWidth = [self arrowSize].width / 2;
	NSPoint leadingPoint, trailingPoint;
	switch ([self arrowDirection]) {
		case SMRPopoverArrowDirectionDown:
			leadingPoint = NSMakePoint(peakPoint.x + halfArrowWidth, currentPoint.y);
			trailingPoint = NSMakePoint(peakPoint.x - halfArrowWidth, currentPoint.y);
			break;
		case SMRPopoverArrowDirectionUp:
			leadingPoint = NSMakePoint(peakPoint.x - halfArrowWidth, currentPoint.y);
			trailingPoint = NSMakePoint(peakPoint.x + halfArrowWidth, currentPoint.y);
			break;
		case SMRPopoverArrowDirectionRight:
			leadingPoint = NSMakePoint(currentPoint.x, peakPoint.y + halfArrowWidth);
			trailingPoint = NSMakePoint(currentPoint.x, peakPoint.y - halfArrowWidth);
			break;
		case SMRPopoverArrowDirectionLeft:
			leadingPoint = NSMakePoint(currentPoint.x, peakPoint.y - halfArrowWidth);
			trailingPoint = NSMakePoint(currentPoint.x, peakPoint.y + halfArrowWidth);
			break;
		default:
			[path lineToPoint:endPoint];
			return;
	}
	[path lineToPoint:leadingPoint];
	[path lineToPoint:peakPoint];
	[path lineToPoint:trailingPoint];
	[path lineToPoint:endPoint];
}

@end
